﻿#include "framework.h"
#include <playsoundapi.h>
#include <time.h>
#include "BlockN.h"
#include "Const.h"
#include "Game.h"
#include "Resource.h"

// 全局方块数组(未被消除的累积方块)
ColorGrid gExistentBlocks[GAME_BOARD_GRID_COUNT_V][GAME_BOARD_GRID_COUNT_H];

// 函数声明
static void Game_Delete(Game*);
static void Game_CheckRow(Game*);
static Block* Game_CreateRandBlock();
static void Game_DrawExistentBlock(Game*);
static void Game_DrawGameBoard(Game*);
static void Game_DrawPreview(Game*);
static void Game_GameOver(Game*, BOOL);
static BOOL Game_IsLost(Game*);
static BOOL Game_IsWon(Game*);
static void Game_MoveBlockDown(Game*);
static void Game_NextBlock(Game*);
static BOOL Game_OnKeyPress(Game*, UINT);
static void Game_OnTimer(Game*);
static void Game_Pause(Game*);
static BOOL Game_PlayMusic(Game*);
static void Game_ReleaseRow(Game*, int);
static void Game_Start(Game*);

/**
 * 创建游戏。
 * @param callback          回调
 * @param gameBoard         游戏区域
 * @param gameBoardDrawer   游戏区域绘图者
 * @param preview           预览区域
 * @param previewDrawer     预览区域绘图者
 * @return 游戏
 */
Game* New_Game(HWND callback, HWND gameBoard, HDC gameBoardDrawer, HWND preview, HDC previewDrawer) {
    Game* game = (Game*)calloc(1, sizeof(Game));

    // 初始化数据
    game->mCallback = callback;
    game->mDifficulty = 1;
    game->mGameBoard = gameBoard;
    game->mGameBoardDrawer = gameBoardDrawer;
    game->mPreview = preview;
    game->mPreviewDrawer = previewDrawer;
    game->mTop = GAME_BOARD_GRID_COUNT_V + 1;

    // 初始化函数
    game->deleteSelf = Game_Delete;
    game->onKeyPressed = Game_OnKeyPress;
    game->onTimer = Game_OnTimer;
    game->pause = Game_Pause;
    game->playMusic = Game_PlayMusic;
    game->start = Game_Start;

    // 画游戏区域
    Game_DrawGameBoard(game);

    // 画预览区域
    Game_DrawPreview(game);

    // 置随机数种子
    srand((UINT)time(NULL));

    return game;
}

/**
 * 删除游戏。
 * @param game  游戏
 */
static void Game_Delete(Game* game) {
    // 删除方块
    if (game->mCurBlock != NULL) {
        free(game->mCurBlock);
    }
    if (game->mNextBlock != NULL) {
        free(game->mNextBlock);
    }

    // 释放内存
    free(game);
}

/**
 * 检查是否存在满行。
 * @param game  游戏
 */
static void Game_CheckRow(Game* game) {
    int i = 0, j = 0;
    int released = 0;   // 消除的行数
    int bottom = game->mCurBlock->getBottom(game->mCurBlock);
    int top = game->mCurBlock->getTop(game->mCurBlock);

    // 更新"有方块的最高行"
    if (top < game->mTop) {
        game->mTop = top;
    }

    // 从有方块的最上面一行，到当前方块所占的最下一行，逐行检查
    for (i = game->mTop; i <= bottom; i++) {
        for (j = 0; j < GAME_BOARD_GRID_COUNT_H; j++) {
            // 如果该行有空的，就跳过该行
            if (!gExistentBlocks[i][j].mUsed) {
                break;
            }
            // 如果该行满了，消除该行
            if (j == GAME_BOARD_GRID_COUNT_H - 1) {
                Game_ReleaseRow(game, i);
                released++;
            }
        }
    }

    if (released > 0) {
        // 因为消行了，所以需要重绘
        Game_DrawGameBoard(game);
        Game_DrawExistentBlock(game);

        // 计算得分
        game->mScore += released * released * 100;

        // 发送通知
        SendMessage(game->mCallback, UM_SCORE, game->mScore, 0);

        // 计算难度
        if (game->mScore / DIFF_BASE + 1 > game->mDifficulty && game->mDifficulty < DIFF_MAX) {
            // 增加难度
            game->mDifficulty++;
            KillTimer(game->mCallback, TIMER_ID);
            SetTimer(game->mCallback, TIMER_ID, TIMER_INIT - TIMER_GROW * (game->mDifficulty - 1), NULL);

            // 发送通知
            SendMessage(game->mCallback, UM_DIFFICULTY_UP, game->mDifficulty, 0);
        }
    }
}

/**
 * 随机创建一种方块。
 * @return 方块
 */
static Block* Game_CreateRandBlock() {
    int random = rand() % 7 + 1;

    switch (random) {
        case 1:
            return New_Block1();
        case 2:
            return New_Block2();
        case 3:
            return New_Block3();
        case 4:
            return New_Block4();
        case 5:
            return New_Block5();
        case 6:
            return New_Block6();
        case 7:
            return New_Block7();
    }

    return New_Block1();
}

/**
 * 画累积的方块。
 * @param game  游戏
 */
static void Game_DrawExistentBlock(Game* game) {
    int i = 0, j = 0;
    RECT rect = { 0 };

    for (i = 0; i < GAME_BOARD_GRID_COUNT_V; i++) {
        for (j = 0; j < GAME_BOARD_GRID_COUNT_H; j++) {
            if (gExistentBlocks[i][j].mUsed) {
                rect.left = GRID_SIZE * j;
                rect.top = GRID_SIZE * i;
                rect.right = GRID_SIZE * (j + 1);
                rect.bottom = GRID_SIZE * (i + 1);
                SetBkColor(game->mGameBoardDrawer, gExistentBlocks[i][j].mColor);
                ExtTextOut(game->mGameBoardDrawer, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
                DrawEdge(game->mGameBoardDrawer, &rect, EDGE_RAISED, BF_RECT);
            }
        }
    }
}

/**
 * 画游戏区域。
 * @param game  游戏
 */
static void Game_DrawGameBoard(Game* game) {
    RECT rect = { 0, 0, GAME_BOARD_WIDTH, GAME_BOARD_HEIGHT };
    SetBkColor(game->mGameBoardDrawer, GAME_BOARD_COLOR);
    ExtTextOut(game->mGameBoardDrawer, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
    InvalidateRect(game->mGameBoard, NULL, FALSE);
}

/**
 * 画预览区域。
 * @param game  游戏
 */
static void Game_DrawPreview(Game* game) {
    RECT rect = { 0, 0, PREVIEW_WIDTH, PREVIEW_HEIGHT };
    SetBkColor(game->mPreviewDrawer, PREVIEW_COLOR);
    ExtTextOut(game->mPreviewDrawer, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
    if (game->mNextBlock != NULL) {
        game->mNextBlock->enterPreview(game->mNextBlock, game->mPreviewDrawer);
    }
    InvalidateRect(game->mPreview, NULL, FALSE);
}

/**
 * 游戏结束。
 * @param game  游戏
 * @param isWon 胜利则为 TRUE，否则为 FALSE
 */
static void Game_GameOver(Game* game, BOOL isWon) {
    // 将状态设置为已结束
    game->mStatus = END;

    // 停止定时器
    KillTimer(game->mCallback, TIMER_ID);

    // 停止播放音乐
    if (game->mEnableMusic) {
        PlaySound(NULL, NULL, 0);
    }

    // 发送通知
    SendMessage(game->mCallback, UM_GAME_OVER, isWon, 0);
}

/**
 * 判断是否失败。
 * @param game  游戏
 * @return 失败则返回 TRUE，否则返回 FALSE
 */
static BOOL Game_IsLost(Game* game) {
    return game->mTop == 0 && gExistentBlocks[0][4].mUsed;
}

/**
 * 判断是否胜利。
 * @param game  游戏
 * @return 胜利则返回 TRUE，否则返回 FALSE
 */
static BOOL Game_IsWon(Game* game) {
    return game->mScore >= WIN_NUMBER;
}

/**
 * 方块下落。
 * @param game  游戏
 */
static void Game_MoveBlockDown(Game* game) {
    BOOL canMoveDown = FALSE;
    BOOL isWon = FALSE;
    BOOL isLost = FALSE;

    if (game->mStatus != PLAYING) {
        return;
    }

    canMoveDown = game->mCurBlock->moveDown(game->mCurBlock);
    if (!canMoveDown) {
        // 检查是否存在满行
        Game_CheckRow(game);

        // 判断是否胜利
        isWon = Game_IsWon(game);
        if (isWon) {
            Game_GameOver(game, TRUE);
            return;
        }

        // 判断是否失败
        isLost = Game_IsLost(game);
        if (isLost) {
            Game_GameOver(game, FALSE);
            return;
        }

        // 产生新的方块
        Game_NextBlock(game);
    }
}

/**
 * 产生新的方块。
 * @param game  游戏
 */
static void Game_NextBlock(Game* game) {
    // 创建当前方块
    if (game->mCurBlock != NULL) {
        free(game->mCurBlock);
        game->mCurBlock = game->mNextBlock;
    } else {
        game->mCurBlock = Game_CreateRandBlock();
    }

    // 创建下一个方块
    game->mNextBlock = Game_CreateRandBlock();

    // 当前方块进入游戏区域
    game->mCurBlock->enterGameBoard(game->mCurBlock, game->mGameBoard, game->mGameBoardDrawer);

    // 下一个方块进入预览区域
    Game_DrawPreview(game);
}

/**
 * 按键事件的响应函数。
 * @param game  游戏
 * @param key   按键
 * @return 事件在此被处理则返回 TRUE，否则返回 FALSE
 */
static BOOL Game_OnKeyPress(Game* game, UINT key) {
    if (game->mStatus != PLAYING) {
        return FALSE;
    }

    switch (key) {
        case VK_DOWN:
            Game_MoveBlockDown(game);
            return TRUE;
        case VK_LEFT:
            game->mCurBlock->moveLeft(game->mCurBlock);
            return TRUE;
        case VK_RIGHT:
            game->mCurBlock->moveRight(game->mCurBlock);
            return TRUE;
        case VK_UP:
            game->mCurBlock->transform(game->mCurBlock);
            return TRUE;
    }

    return FALSE;
}

/**
 * 定时器事件的响应函数。
 * @param game  游戏
 */
static void Game_OnTimer(Game* game) {
    Game_MoveBlockDown(game);
}

/**
 * 游戏暂停。
 * @param game  游戏
 */
static void Game_Pause(Game* game) {
    if (game->mStatus == PLAYING) {
        // 停止定时器
        KillTimer(game->mCallback, TIMER_ID);

        // 停止播放音乐
        if (game->mEnableMusic) {
            PlaySound(NULL, NULL, 0);
        }

        // 将状态设置为已暂停
        game->mStatus = PAUSED;
    } else if (game->mStatus == PAUSED) {
        // 开启定时器
        SetTimer(game->mCallback, TIMER_ID, TIMER_INIT - TIMER_GROW * (game->mDifficulty - 1), NULL);

        // 播放音乐
        if (game->mEnableMusic) {
            PlaySound(MAKEINTRESOURCE(IDR_MUSIC), NULL, SND_RESOURCE | SND_ASYNC | SND_NODEFAULT | SND_LOOP);
        }

        // 将状态设置为游戏中
        game->mStatus = PLAYING;
    }
}

/**
 * 播放音乐。
 * @param game  游戏
 * @return 正在播放音乐则为 TRUE，否则为 FALSE
 */
static BOOL Game_PlayMusic(Game* game) {
    game->mEnableMusic = !game->mEnableMusic;

    if (game->mEnableMusic) {
        if (game->mStatus == PLAYING) {
            PlaySound(MAKEINTRESOURCE(IDR_MUSIC), NULL, SND_RESOURCE | SND_ASYNC | SND_NODEFAULT | SND_LOOP);
        }
    } else {
        PlaySound(NULL, NULL, 0);
    }

    return game->mEnableMusic;
}

/**
 * 消行。
 * @param game  游戏
 * @param row   行号
 */
static void Game_ReleaseRow(Game* game, int row) {
    // 最上行
    if (row == 0) {
        for (int j = 0; j < GAME_BOARD_GRID_COUNT_H; j++) {
            gExistentBlocks[row][j].mUsed = FALSE;
        }
        game->mTop++;
        return;
    }

    // 非最上行
    for (int i = row; i > game->mTop; i--) {
        for (int j = 0; j < GAME_BOARD_GRID_COUNT_H; j++) {
            gExistentBlocks[i][j] = gExistentBlocks[i - 1][j];
        }
    }

    for (int j = 0; j < GAME_BOARD_GRID_COUNT_H; j++) {
        gExistentBlocks[game->mTop][j].mUsed = FALSE;
    }

    game->mTop++;
}

/**
 * 游戏开始。
 * @param game  游戏
 */
static void Game_Start(Game* game) {
    // 画游戏区域
    Game_DrawGameBoard(game);

    // 画预览区域
    Game_DrawPreview(game);

    // 创建方块
    ZeroMemory(gExistentBlocks, sizeof(gExistentBlocks));
    if (game->mCurBlock != NULL) {
        free(game->mCurBlock);
        game->mCurBlock = NULL;
    }
    if (game->mNextBlock != NULL) {
        free(game->mNextBlock);
        game->mNextBlock = NULL;
    }
    Game_NextBlock(game);

    // 重置难度
    game->mDifficulty = 1;

    // 重置分数
    game->mScore = 0;

    // 开启定时器
    KillTimer(game->mCallback, TIMER_ID);
    SetTimer(game->mCallback, TIMER_ID, TIMER_INIT, NULL);

    // 播放音乐
    if (game->mEnableMusic) {
        PlaySound(MAKEINTRESOURCE(IDR_MUSIC), NULL, SND_RESOURCE | SND_ASYNC | SND_NODEFAULT | SND_LOOP);
    }

    // 将状态设置为游戏中
    game->mStatus = PLAYING;
}
